package com.example.translate;

import com.example.utils.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.core.CountRequest;
import org.elasticsearch.client.core.CountResponse;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

@Service
@Slf4j
public class TranslateService {
    public static final String TRANSLATE_KEY = "translate";
    @Resource
    RestHighLevelClient client;

    public Page<Map<String, Object>> translate(Pageable pageable, TranslateDTO dto) throws IOException {
        List<Map<String, Object>> list = searchList(pageable, dto);
        long searchCount = searchCount(dto);
        return new PageImpl<>(list, pageable, searchCount);
    }

    private long searchCount(TranslateDTO dto) throws IOException {
        CountRequest countRequest = new CountRequest(TRANSLATE_KEY);
        SearchSourceBuilder searchSourceBuilder = searchSourceBuilder(dto);
        countRequest.query(searchSourceBuilder.query());
        CountResponse countResponse = client.count(countRequest, RequestOptions.DEFAULT);
        return countResponse.getCount();
    }

    private List<Map<String, Object>> searchList(Pageable pageable, TranslateDTO dto) throws IOException {
        SearchRequest searchRequest = new SearchRequest(TRANSLATE_KEY);
        SearchSourceBuilder sourceBuilder = searchSourceBuilder(dto);
        sourceBuilder.from(pageable.getPageNumber() * pageable.getPageSize());
        sourceBuilder.size(pageable.getPageSize());
        for (Sort.Order order : pageable.getSort()) {
            SortOrder sortOrder = null;
            if (order.getDirection() == Sort.Direction.ASC) {
                sortOrder = SortOrder.ASC;
            } else if (order.getDirection() == Sort.Direction.DESC) {
                sortOrder = SortOrder.DESC;
            }
            sourceBuilder.sort(order.getProperty(), sortOrder);
        }
        searchRequest.source(sourceBuilder);
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        SearchHits hits = searchResponse.getHits();
        return Arrays.stream(hits.getHits()).map(SearchHit::getSourceAsMap).collect(Collectors.toList());
    }

    private SearchSourceBuilder searchSourceBuilder(TranslateDTO dto) {
        SearchSourceBuilder source = new SearchSourceBuilder();
        switch (dto.getType()) {
            case cn2en: {
                source.query(QueryBuilders.termQuery("cn", dto.getCn()));
                break;
            }
            case en2cn: {
                source.query(QueryBuilders.termQuery("en", dto.getEn()));
                break;
            }
            default: {
            }
        }
        return source;
    }

    public void save(TranslateDTO dto) throws IOException {
        IndexRequest indexRequest = new IndexRequest(TRANSLATE_KEY);
        if (ObjectUtils.isEmpty(dto.getId())) {
            dto.setId(UUID.randomUUID().toString());
        }
        indexRequest.id(dto.getId());
        indexRequest.source(JsonUtils.toJson(dto), XContentType.JSON);
        IndexResponse indexResponse = client.index(indexRequest, RequestOptions.DEFAULT);
        indexResponse.status();
    }

    public void delete(String en, String cn) throws IOException {
        DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest();
        deleteByQueryRequest.indices(TRANSLATE_KEY);
        TermQueryBuilder termQueryBuilder;
        if (ObjectUtils.isEmpty(en)) {
            termQueryBuilder = new TermQueryBuilder("cn", cn);
        } else {
            termQueryBuilder = new TermQueryBuilder("en", en);
        }
        deleteByQueryRequest.setQuery(termQueryBuilder);
        client.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT);
    }

    public Map<String, Object> search(Pageable pageable, String content) throws IOException {
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        HighlightBuilder highlightBuilder = highlightBuilder();
        addContentQuery(content, query);
        SearchRequest searchRequest = searchRequest(pageable, query, highlightBuilder);
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        return JsonUtils.toMap(searchResponse.toString());
    }

    private void addContentQuery(String content, BoolQueryBuilder query) {
        if (StringUtils.hasText(content)) {
            query.must(QueryBuilders.multiMatchQuery(content, "cn", "en"));
        } else {
            query.must(QueryBuilders.matchAllQuery());
        }
    }

    private HighlightBuilder highlightBuilder() {
        HighlightBuilder highlightBuilder = new HighlightBuilder().preTags("<span class=\"es-highlight\">").postTags("</span>");
        highlightBuilder.field("en");
        highlightBuilder.field("cn");
        return highlightBuilder;
    }

    private SearchRequest searchRequest(Pageable pageable, BoolQueryBuilder query, HighlightBuilder highlightBuilder) {
        SearchRequest searchRequest = new SearchRequest(TRANSLATE_KEY);
        SearchSourceBuilder source = new SearchSourceBuilder();
        source.from(pageable.getPageNumber() * pageable.getPageSize());
        source.size(pageable.getPageSize());
        source.query(query);
        source.highlighter(highlightBuilder);
        searchRequest.source(source);
        return searchRequest;
    }

    public void delete(String id) throws IOException {
        DeleteRequest deleteRequest = new DeleteRequest(TRANSLATE_KEY, id);
        client.delete(deleteRequest, RequestOptions.DEFAULT);
    }
}
